package com.qhhc.service;

import com.alibaba.dashscope.audio.asr.transcription.*;
import com.google.gson.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import ws.schild.jave.Encoder;
import ws.schild.jave.EncoderException;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.List;

import jakarta.annotation.PostConstruct;
import org.springframework.scheduling.annotation.Scheduled;

@Service
public class VoiceService {

    @Autowired
    private OSSService ossService;

    @Value("${use.local.sensevoice:false}")
    private boolean useLocalSenseVoice;

    @Value("${sensevoice.local.url:http://localhost:8000/api/v1/asr}")
    private String localSenseVoiceUrl;

    @Value("${local.audio.directory:src/main/resources/audios}")
    private String localAudioDirectory;

    @Value("${temp.audio.directory:src/main/resources/temp}")
    private String tempAudioDirectory;

    public String processAudio(MultipartFile audioFile) {
        try {
            if (useLocalSenseVoice) {
                return processAudioWithLocalSenseVoice(audioFile);
            } else {
                return processAudioWithDashScope(audioFile);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }

    public String processAudioFromLocal(MultipartFile audioFile) {
        try {
            // 保存文件到本地目录
            String fileName = saveAudioFileLocally(audioFile);

            // 转换成WAV格式
            String wavFileName = convertToWav(fileName);

            // 使用转换后的文件处理
            return processSavedAudioFile(wavFileName);
        } catch (Exception e) {
            e.printStackTrace();
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }

    private String saveAudioFileLocally(MultipartFile audioFile) throws IOException {
        // 确保目录存在
        Path directory = Paths.get(tempAudioDirectory);
        if (!Files.exists(directory)) {
            Files.createDirectories(directory);
        }

        // 生成文件名
        String originalFilename = audioFile.getOriginalFilename();
        String fileName = System.currentTimeMillis() + "_" +
                (originalFilename != null ? originalFilename : "recording.wav");

        // 保存文件
        Path targetPath = directory.resolve(fileName);
        Files.copy(audioFile.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);

        System.out.println("文件已保存到: " + targetPath.toAbsolutePath());
        return fileName;
    }

    private String convertToWav(String fileName) throws Exception {
        File source = new File(Paths.get(tempAudioDirectory, fileName).toString());

        // 校验源文件
        if (!source.exists() || source.length() == 0) {
            throw new IOException("源文件不存在或为空: " + source.getAbsolutePath());
        }

        // 创建目标WAV文件名
        String wavFileName = "converted_" + fileName;
        if (!wavFileName.endsWith(".wav")) {
            wavFileName = wavFileName + ".wav";
        }

        File target = new File(Paths.get(tempAudioDirectory, wavFileName).toString());

        // 如果输入文件太小（流式传输的小片段），处理时可能会遇到问题
        // 对于小于10KB的文件，我们可以考虑返回空结果或跳过处理
        if (source.length() < 1024) { // 小于1KB的文件
            System.out.println("警告: 文件过小 (" + source.length() + " 字节)，可能无法正确处理");
        }

        // 对于已经是WAV格式且格式正确的文件，直接返回
        if (isValidWavFile(source)) {
            System.out.println("文件已经是有效的WAV格式: " + source.getAbsolutePath());
            return fileName;
        }

        try {
            // 设置音频属性
            AudioAttributes audio = new AudioAttributes();
            audio.setCodec("pcm_s16le"); // 使用PCM编码，这是WAV的标准编码
            audio.setBitRate(128000);
            audio.setChannels(1); // 单声道
            audio.setSamplingRate(16000); // 16kHz采样率

            // 设置编码属性
            EncodingAttributes attrs = new EncodingAttributes();
            attrs.setOutputFormat("wav");
            attrs.setAudioAttributes(audio);

            // 执行转换
            try {
                Encoder encoder = new Encoder();
                encoder.encode(new MultimediaObject(source), target, attrs);
                System.out.println("文件已转换: " + target.getAbsolutePath());
                return wavFileName;
            } catch (EncoderException e) {
                System.err.println("转换失败: " + e.getMessage());

                // 如果是流式处理的小片段且转换失败，我们可以尝试直接返回一个空结果
                if (source.length() < 10240) { // 小于10KB
                    System.out.println("文件过小，跳过处理");
                    // 创建一个空的WAV文件
                    return createEmptyWavFile(wavFileName);
                }

                // 如果转换失败，尝试使用简单复制
                Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING);
                return wavFileName;
            }
        } catch (Exception e) {
            System.err.println("处理音频文件出错: " + e.getMessage());
            e.printStackTrace();
            throw e;
        }
    }

    /**
     * 创建一个空的WAV文件作为占位符
     */
    private String createEmptyWavFile(String fileName) throws IOException {
        // 创建一个简单的空WAV文件（仅包含头部）
        String emptyWavPath = Paths.get(tempAudioDirectory, "empty_" + fileName).toString();
        File emptyWavFile = new File(emptyWavPath);

        try (FileOutputStream fos = new FileOutputStream(emptyWavFile)) {
            // 写入WAV文件头 (44字节)
            // RIFF头
            fos.write(new byte[] { 'R', 'I', 'F', 'F' });
            // 文件大小-8，这里是36（44-8）表示一个空的数据块
            fos.write(new byte[] { 36, 0, 0, 0 });
            // WAVE标识
            fos.write(new byte[] { 'W', 'A', 'V', 'E' });
            // fmt子块
            fos.write(new byte[] { 'f', 'm', 't', ' ' });
            // fmt子块大小
            fos.write(new byte[] { 16, 0, 0, 0 });
            // 音频格式，1表示PCM
            fos.write(new byte[] { 1, 0 });
            // 通道数，1表示单声道
            fos.write(new byte[] { 1, 0 });
            // 采样率，16000Hz
            fos.write(new byte[] { (byte) 0x80, (byte) 0x3E, 0, 0 });
            // 字节率：采样率 × 通道数 × 位深 / 8
            fos.write(new byte[] { 0, (byte) 0x7D, 0, 0 });
            // 块对齐：通道数 × 位深 / 8
            fos.write(new byte[] { 2, 0 });
            // 位深：16位
            fos.write(new byte[] { 16, 0 });
            // 数据子块
            fos.write(new byte[] { 'd', 'a', 't', 'a' });
            // 音频数据大小：0（无音频数据）
            fos.write(new byte[] { 0, 0, 0, 0 });
        }

        System.out.println("已创建空白WAV文件: " + emptyWavFile.getAbsolutePath());
        return "empty_" + fileName;
    }

    private boolean isValidWavFile(File file) {
        // 检查文件格式是否为有效的WAV
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] header = new byte[12];
            if (fis.read(header) != 12) {
                return false;
            }

            // 检查WAV文件头
            String riff = new String(header, 0, 4);
            String wave = new String(header, 8, 4);

            return "RIFF".equals(riff) && "WAVE".equals(wave);
        } catch (Exception e) {
            return false;
        }
    }

    private String processSavedAudioFile(String fileName) {
        try {
            // 构建完整的文件路径
            Path audioPath = Paths.get(tempAudioDirectory, fileName);
            String absolutePath = audioPath.toAbsolutePath().toString();

            System.out.println("使用文件处理: " + absolutePath);

            // 如果使用本地SenseVoice
            if (useLocalSenseVoice) {
                // 创建RestTemplate对象
                RestTemplate restTemplate = new RestTemplate();

                // 设置请求头
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.MULTIPART_FORM_DATA);

                // 创建请求参数
                MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
                body.add("files", new FileSystemResource(absolutePath));
                body.add("keys", "recording");
                body.add("lang", "auto");

                // 创建HttpEntity对象，将请求头和请求体组合
                HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

                // 发送POST请求并获取响应结果
                ResponseEntity<String> response = restTemplate.postForEntity(
                        localSenseVoiceUrl,
                        requestEntity,
                        String.class);

                // 处理响应结果
                String responseBody = response.getBody();

                // 解析JSON结果
                JsonObject jsonResponse = new Gson().fromJson(responseBody, JsonObject.class);

                // 从result数组中获取第一个元素的text
                if (jsonResponse.has("result") && jsonResponse.get("result").isJsonArray()
                        && jsonResponse.getAsJsonArray("result").size() > 0) {
                    JsonObject firstResult = jsonResponse.getAsJsonArray("result").get(0).getAsJsonObject();
                    if (firstResult.has("text")) {
                        return firstResult.get("text").getAsString();
                    }
                }

                return "{\"error\": \"无法从SenseVoice获取有效结果\"}";
            } else {
                // 如果使用DashScope，先上传文件到OSS
                String fileUrl = ossService.uploadLocalFile(absolutePath);
                System.out.println("文件已上传至OSS，访问URL: " + fileUrl);

                // 构建参数并调用API
                TranscriptionParam param = TranscriptionParam.builder()
                        .model("sensevoice-v1")
                        .fileUrls(Arrays.asList(fileUrl))
                        .parameter("language_hints", new String[] { "zh" })
                        .build();

                Transcription transcription = new Transcription();
                TranscriptionResult result = transcription.asyncCall(param);
                result = transcription.wait(
                        TranscriptionQueryParam.FromTranscriptionParam(param, result.getTaskId()));

                // 获取语音识别结果
                List<TranscriptionTaskResult> taskResultList = result.getResults();
                if (taskResultList != null && taskResultList.size() > 0) {
                    TranscriptionTaskResult taskResult = taskResultList.get(0);
                    String transcriptionUrl = taskResult.getTranscriptionUrl();

                    HttpURLConnection connection = (HttpURLConnection) new URL(transcriptionUrl).openConnection();
                    connection.setRequestMethod("GET");
                    connection.connect();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    return response.toString();
                }

                return "{\"error\": \"无法处理语音文件\"}";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "{\"error\": \"" + e.getMessage() + "\"}";
        }
    }

    private String processAudioWithLocalSenseVoice(MultipartFile audioFile) throws IOException {
        try {
            // 保存文件到临时目录
            String fileName = saveAudioFileLocally(audioFile);

            // 转换成WAV格式
            String wavFileName = convertToWav(fileName);

            // 构建完整的文件路径
            Path audioPath = Paths.get(tempAudioDirectory, wavFileName);
            String absolutePath = audioPath.toAbsolutePath().toString();

            System.out.println("使用本地SenseVoice处理文件: " + absolutePath);

            // 创建RestTemplate对象
            RestTemplate restTemplate = new RestTemplate();

            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);

            // 创建请求参数
            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add("files", new FileSystemResource(absolutePath));
            body.add("keys", "recording");
            body.add("lang", "auto");

            // 创建HttpEntity对象，将请求头和请求体组合
            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

            try {
                // 发送POST请求并获取响应结果
                ResponseEntity<String> response = restTemplate.postForEntity(
                        localSenseVoiceUrl,
                        requestEntity,
                        String.class);

                // 处理响应结果
                String responseBody = response.getBody();

                // 解析JSON结果
                JsonObject jsonResponse = new Gson().fromJson(responseBody, JsonObject.class);

                // 从result数组中获取第一个元素的text
                if (jsonResponse.has("result") && jsonResponse.get("result").isJsonArray()
                        && jsonResponse.getAsJsonArray("result").size() > 0) {
                    JsonObject firstResult = jsonResponse.getAsJsonArray("result").get(0).getAsJsonObject();
                    if (firstResult.has("text")) {
                        return firstResult.get("text").getAsString();
                    }
                }

                return "{\"error\": \"无法从SenseVoice获取有效结果\"}";
            } catch (Exception e) {
                System.err.println("调用SenseVoice API失败: " + e.getMessage());
                e.printStackTrace();
                return "{\"error\": \"调用SenseVoice API失败: " + e.getMessage() + "\"}";
            }
        } catch (Exception e) {
            System.err.println("处理音频文件失败: " + e.getMessage());
            e.printStackTrace();
            return "{\"error\": \"处理音频文件失败: " + e.getMessage() + "\"}";
        }
    }

    private String processAudioWithDashScope(MultipartFile audioFile) throws Exception {
        // 上传文件到OSS并获取可访问的URL
        String fileUrl = ossService.uploadFile(audioFile);
        System.out.println("音频文件已上传至OSS，访问URL: " + fileUrl);

        // 使用SenseVoice API处理语音文件
        TranscriptionParam param = TranscriptionParam.builder()
                .model("sensevoice-v1")
                .fileUrls(Arrays.asList(fileUrl))
                .parameter("language_hints", new String[] { "zh" })
                .build();

        Transcription transcription = new Transcription();
        // 提交任务
        TranscriptionResult result = transcription.asyncCall(param);
        // 等待任务完成
        result = transcription.wait(
                TranscriptionQueryParam.FromTranscriptionParam(param, result.getTaskId()));

        // 获取语音识别结果
        List<TranscriptionTaskResult> taskResultList = result.getResults();
        if (taskResultList != null && taskResultList.size() > 0) {
            TranscriptionTaskResult taskResult = taskResultList.get(0);
            // 获取识别结果的url
            String transcriptionUrl = taskResult.getTranscriptionUrl();
            // 获取url内对应的结果
            HttpURLConnection connection = (HttpURLConnection) new URL(transcriptionUrl).openConnection();
            connection.setRequestMethod("GET");
            connection.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

            // 直接返回原始JSON结果，让前端处理
            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            return response.toString();
        }

        return "{\"error\": \"无法处理语音文件\"}";
    }

    /**
     * 处理流式音频数据
     * 
     * @param audioFile       音频文件
     * @param useLocalStorage 是否使用本地存储
     * @param isFinal         是否为最终数据片段
     * @return 识别结果
     */
    public String processStreamingAudio(MultipartFile audioFile, boolean useLocalStorage, boolean isFinal) {
        try {
            System.out.println("接收到" + (isFinal ? "最终" : "流式") + "片段音频，大小: " + audioFile.getSize() + " 字节");

            // 对于最终片段的处理可能需要用完整的逻辑
            if (isFinal) {
                // 如果是最终片段，使用标准处理方法
                return useLocalStorage ? processAudioFromLocal(audioFile) : processAudio(audioFile);
            }

            // 以下是流式片段处理
            // 流式片段我们也使用完整处理逻辑，但返回进度化的结果
            try {
                String result;
                if (useLocalStorage) {
                    // 保存文件到临时目录
                    String fileName = saveAudioFileLocally(audioFile);
                    // 转换成WAV格式
                    String wavFileName = convertToWav(fileName);
                    // 构建完整的文件路径
                    Path audioPath = Paths.get(tempAudioDirectory, wavFileName);
                    String absolutePath = audioPath.toAbsolutePath().toString();
                    File audioFile2 = new File(absolutePath);

                    // 如果文件不存在或太小，返回空结果
                    if (!audioFile2.exists() || audioFile2.length() < 2048) {
                        return "{\"text\": \"\"}";
                    }

                    // 使用本地SenseVoice API处理
                    result = processSavedAudioFileForStreaming(wavFileName);
                } else {
                    // 使用阿里云API处理
                    result = processAudioWithDashScope(audioFile);
                }

                // 从结果中提取有用的部分文本，模拟流式效果
                return processStreamingResult(result, false);
            } catch (Exception e) {
                System.out.println("流式片段处理失败，返回空结果: " + e.getMessage());
                return "{\"text\": \"\"}";
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 对于非最终片段的错误，返回空文本
            if (!isFinal) {
                return "{\"text\": \"\"}";
            }
            return "{\"error\": \"处理流式音频失败: " + e.getMessage() + "\"}";
        }
    }

    /**
     * 处理保存的音频文件，用于流式处理
     */
    private String processSavedAudioFileForStreaming(String fileName) {
        try {
            // 构建完整的文件路径
            Path audioPath = Paths.get(tempAudioDirectory, fileName);
            String absolutePath = audioPath.toAbsolutePath().toString();

            System.out.println("使用本地SenseVoice处理流式文件: " + absolutePath);

            // 创建RestTemplate对象
            RestTemplate restTemplate = new RestTemplate();

            // 设置请求头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.MULTIPART_FORM_DATA);

            // 创建请求参数
            MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
            body.add("files", new FileSystemResource(absolutePath));
            body.add("keys", "recording");
            body.add("lang", "auto");

            // 创建HttpEntity对象，将请求头和请求体组合
            HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);

            try {
                // 发送POST请求并获取响应结果
                ResponseEntity<String> response = restTemplate.postForEntity(
                        localSenseVoiceUrl,
                        requestEntity,
                        String.class);

                // 处理响应结果
                return response.getBody();
            } catch (Exception e) {
                System.err.println("调用SenseVoice API失败: " + e.getMessage());
                e.printStackTrace();
                return "{\"error\": \"调用SenseVoice API失败: " + e.getMessage() + "\"}";
            }
        } catch (Exception e) {
            System.err.println("处理音频文件失败: " + e.getMessage());
            e.printStackTrace();
            return "{\"error\": \"处理音频文件失败: " + e.getMessage() + "\"}";
        }
    }

    /**
     * 根据是否为最终结果处理识别文本
     * 
     * @param result  原始识别结果
     * @param isFinal 是否为最终结果
     * @return 处理后的结果
     */
    private String processStreamingResult(String result, boolean isFinal) {
        try {
            if (result == null || result.isEmpty()) {
                return "{\"text\": \"\"}";
            }

            // 解析原始结果
            Gson gson = new Gson();
            JsonObject jsonObject = gson.fromJson(result, JsonObject.class);

            // 提取文本内容
            if (jsonObject.has("result") && jsonObject.get("result").isJsonArray()
                    && jsonObject.getAsJsonArray("result").size() > 0) {
                JsonObject firstResult = jsonObject.getAsJsonArray("result").get(0).getAsJsonObject();
                if (firstResult.has("text")) {
                    String fullText = firstResult.get("text").getAsString();

                    // 在非最终结果时，返回部分文本模拟流式效果
                    if (!isFinal && fullText.length() > 5) {
                        // 根据文本长度，随机返回不同长度的部分文本
                        // 这样每次结果会不同，给用户流式输出的感觉
                        int textLength = fullText.length();
                        int endPos;

                        // 针对长度进行动态计算
                        if (textLength <= 10) {
                            endPos = textLength; // 短文本完整返回
                        } else {
                            // 返回60%-90%的内容，随机决定
                            double percentage = 0.6 + Math.random() * 0.3;
                            endPos = (int) (textLength * percentage);
                        }

                        String partialText = fullText.substring(0, endPos);
                        return "{\"text\": \"" + partialText + "\"}";
                    }

                    // 最终结果返回完整文本
                    return "{\"text\": \"" + fullText + "\"}";
                }
            }

            // 无法解析结果，返回空
            return "{\"text\": \"\"}";
        } catch (Exception e) {
            System.err.println("处理识别结果失败: " + e.getMessage());
            return isFinal ? "{\"error\": \"" + e.getMessage() + "\"}" : "{\"text\": \"\"}";
        }
    }

    private static class FileSystemResource extends ByteArrayResource {
        private final String filename;

        public FileSystemResource(String path) throws IOException {
            super(Files.readAllBytes(Paths.get(path)));
            this.filename = Paths.get(path).getFileName().toString();
        }

        @Override
        public String getFilename() {
            return filename;
        }
    }

    @PostConstruct
    public void initialize() {
        // 确保临时目录存在
        try {
            Path tempDir = Paths.get(tempAudioDirectory);
            if (!Files.exists(tempDir)) {
                Files.createDirectories(tempDir);
            }
            System.out.println("临时音频目录已创建: " + tempDir.toAbsolutePath());

            // 同时确保永久存储目录存在
            Path audioDir = Paths.get(localAudioDirectory);
            if (!Files.exists(audioDir)) {
                Files.createDirectories(audioDir);
            }
            System.out.println("永久音频目录已创建: " + audioDir.toAbsolutePath());
        } catch (IOException e) {
            System.err.println("无法创建音频目录: " + e.getMessage());
        }
    }

    // 清理临时文件的方法
    @Scheduled(fixedRate = 3600000) // 每小时执行一次
    public void cleanupTempFiles() {
        try {
            Path tempDir = Paths.get(tempAudioDirectory);
            if (Files.exists(tempDir)) {
                Files.walk(tempDir)
                        .filter(path -> !Files.isDirectory(path))
                        .filter(path -> {
                            try {
                                // 删除超过24小时的文件
                                return Files.getLastModifiedTime(path).toInstant()
                                        .isBefore(Instant.now().minus(24, ChronoUnit.HOURS));
                            } catch (IOException e) {
                                return false;
                            }
                        })
                        .forEach(path -> {
                            try {
                                Files.delete(path);
                                System.out.println("已删除临时文件: " + path);
                            } catch (IOException e) {
                                System.err.println("无法删除临时文件: " + path);
                            }
                        });
            }
        } catch (IOException e) {
            System.err.println("清理临时文件时出错: " + e.getMessage());
        }
    }
}